package jude.java.money.seo;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LinkList {
	private static final int DOT = 1000;
	private static final String ACTION_SEPARATE = "separate";		//数据分离
	private static final String ACTION_UNIQUE = "unique";			//清除重复的
	
	private static final String SORT_DESC = "desc";
	private static final String SORT_ASC = "asc";
	
	private static final String KEY_PATH = "path";
	private static final String KEY_ACTION = "action";
	private static final String KEY_FILE1 = "file1";
	private static final String KEY_FILE2 = "file2";
	private static final String KEY_PATTERN1 = "pattern1";
	private static final String KEY_PATTERN2 = "pattern2";
	private static final String KEY_FILE_PATTERN1 = "fp1";
	private static final String KEY_FILE_PATTERN2 = "fp2";
	private static final String KEY_TRIM = "trim";
	private static final String KEY_SORT = "sort";
	
	private String targetPath = "./";
	private String action = ACTION_SEPARATE;
	private String file1, file2;
	private String pattern1;
	private String pattern2;
	private boolean trim;
	private String sort;
	
	private Map<String, String> params;
	private Collection<String> lines;
	
	public LinkList() {
	}
	
	public LinkList(Map<String, String> params) {
		this.params = params;
//		for (Map.Entry<String, String> e : params.entrySet()) {
//			System.out.println(e.getKey() + "=" + e.getValue());
//		}
	}
	
	public void process() throws IOException {
		if (ACTION_SEPARATE.equals(this.action))
			processSeparator();
		else if (ACTION_UNIQUE.equals(this.action))
			processUnique();
	}
	
	private void processUnique() throws IOException {
		String[] paths = getPaths(file1, null);
		if (paths == null)
			return;
		System.out.println("process file: " + file1);
		BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(new File(paths[0])), "UTF-8"));
		BufferedWriter out1 = new BufferedWriter(new FileWriter(new File(paths[2])));
		BufferedWriter out2 = new BufferedWriter(new FileWriter(new File(paths[3])));
		String line = null;
		int n = 0, n1 = 0, n2 = 0, n3 = 0;
		String regex = this.pattern1;
		Pattern p = isEmpty(regex) ? null : Pattern.compile(regex);
		Set<String> lines = new HashSet<String>();
		while ((line = in.readLine()) != null) {
			if (++n % DOT == 0) {
				System.out.print('.');
				if (n % (DOT * 50) == 0)
					System.out.println();
			}
			if (trim)
				line = line.trim();
			if (p == null) {
				if (lines.contains(line)) {
					out1.write(line);
					out1.write("\r\n");
					n1 ++;
				} else {
					lines.add(line);
					out2.write(line);
					out2.write("\r\n");
					n2 ++;
				}
			} else {
				Matcher m = p.matcher(line);
				if (m.find()) {
					String s = m.groupCount() > 0 ? m.group(1) : m.group(0);
					if (lines.contains(s)) {
						out1.write(line);
						out1.write("\r\n");
						n1 ++;
					} else {
						out2.write(line);
						out2.write("\r\n");
						lines.add(s);
						n2 ++;
					}
				} else {
//					System.out.println(line);
					n3 ++;
				}
			}
		}
		in.close();
		out1.close();
		out2.close();
		if (n > DOT)
			System.out.println();
		System.out.println("Total:     " + n);
		System.out.println("Unique:    " + n2);
		System.out.println("Repeat:    " + n1);
		if (p != null)
			System.out.println("Not match: " + n3);
	}
	
	private void processSeparator() throws IOException {
		sort();
		String[] paths = getPaths(file1, file2);
		if (paths == null)
			return;
		System.out.println("process files: " + file1 + ", " + file2);
		readRight(paths[1]);
		BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(new File(paths[0])), "UTF-8"));
		BufferedWriter out1 = new BufferedWriter(new FileWriter(new File(paths[2])));
		BufferedWriter out2 = new BufferedWriter(new FileWriter(new File(paths[3])));
		String line = null;
		int n = 0, n1 = 0, n2 = 0;
		String regex = this.pattern1;
		Pattern p = isEmpty(regex) ? null : Pattern.compile(regex);
//		out:
		while ((line = in.readLine()) != null) {
			if (++n % DOT == 0) {
				System.out.print('.');
				if (n % (DOT * 50) == 0)
					System.out.println();
			}
			if (trim)
				line = line.trim();
			if (p == null) {
				if (lines.contains(line)) {
					out1.write(line);
					out1.write("\r\n");
					n1 ++;
				} else {
					out2.write(line);
					out2.write("\r\n");
					n2 ++;
				}
			} else {
				Matcher m = p.matcher(line);
				if (m.find()) {
					String s = m.groupCount() > 0 ? m.group(1) : m.group(0);
					if (lines.contains(s)) {
						out1.write(line);
						out1.write("\r\n");
						n1 ++;
					} else {
						out2.write(line);
						out2.write("\r\n");
						n2 ++;
					}
				}
			}
		}
		in.close();
		out1.close();
		out2.close();
		if (n > DOT)
			System.out.println();
		System.out.println("Left:      " + n);
		System.out.println("Right:     " + lines.size());
		System.out.println("Same:      " + n1);
		System.out.println("Left Only: " + n2);
	}
	
	public static void main(String[] args) throws IOException {
		Map<String, String> params = getParams(args);
		LinkList l = new LinkList(params);
		l.detect();
		l.process();
		System.out.println();
	}
	
	private void readRight(String file) throws IOException {
		lines = new HashSet<String>();
		BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(file)), "UTF-8"));
		String line = null;
		String regex = this.pattern2;
		Pattern p = isEmpty(regex) ? null : Pattern.compile(regex);
		int n = 0;
		while ((line = reader.readLine()) != null) {
			if (++n % DOT == 0) {
				System.out.print(',');
				if (n % (DOT * 50) == 0)
					System.out.println();
			}
			if (trim)
				line = line.trim();
			if (p != null) {
				Matcher m = p.matcher(line);
				if (m.find() && m.groupCount() > 0) {
					lines.add(m.group(1));
				}
			} else {
				lines.add(line);
			}
		}
		if (n > DOT)
			System.out.println();
		reader.close();
	}
	
	private void detect() {
		String path = params.get(KEY_PATH);
		if (path != null) {
			if (new File(path).isDirectory())
				this.targetPath = path + "/";
			else {
				error("invalid path: " + path);
				return;
			}
			this.targetPath = path;
		}
		path = path == null ? this.targetPath : path;
		
		String action = params.get(KEY_ACTION);
		if (action != null) {
			if (!action.equals(ACTION_SEPARATE) && !action.equals(ACTION_UNIQUE)) {
				error("invalid action: " + action);
				return;
			}
			this.action = action;
		}
		
		boolean two = ACTION_SEPARATE.equals(this.action);		//是否处理两文件交集
		String file1 = params.get(KEY_FILE1);
		String file2 = params.get(KEY_FILE2);
		if (file1 != null && !new File(path + file1).isFile()) {
			error("invalid file1: " + file1);
			return;
		}
		if (file2 != null && !new File(path + file2).isFile()) {
			error("invalid file2: " + file2);
			return;
		}
		if (two && file1 != null && file1.equals(file2)) {
			error("file1 same with file2: " + file1);
			return;
		}
		
		String[] files1 = null;
		if (file1 == null) {
			final String fp1 = params.get(KEY_FILE_PATTERN1);
			if (fp1 == null) {
				error("file1 or filePattern1 must be setted");
				return;
			}
			files1 = new File(path).list(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.matches(fp1);
				}
			});
			if (files1 == null || files1.length == 0) {
				error("no file match: " + fp1);
				return;
			}
			if (two) {
				if (file2 != null) {
					for (int i = 0; i < files1.length; i++) {
						if (!file2.equals(files1[i])) {
							file1 = files1[i];
							break;
						}
					}
				}
			} else {
				if (files1.length == 1)
					file1 = files1[0];
				else {
					for (int i = 0; i < files1.length; i++) {
						String[] paths = getPaths(files1[i], null);
						if (paths != null) {
							if (new File(paths[2]).exists() && new File(paths[3]).exists())
								continue;
							file1 = files1[i];
						}
					}
					if (file1 == null)
						file1 = files1[0];
				}
			}
		}
		if (two && file2 == null) {
			final String fp2 = params.get(KEY_FILE_PATTERN2);
			if (fp2 == null) {
				error("file2 or filePattern2 must be setted");
				return;
			}
			String tfile1 = null;
			String tfile2 = null;
			if (files1 != null && files1.length > 0 && fp2.indexOf("<1>") >= 0) {
				Pattern p = Pattern.compile(params.get(KEY_FILE_PATTERN1));
				out:
				for (int i = 0; i < files1.length; i++) {
					Matcher m = p.matcher(files1[i]);
					if (m.find()) {
						if (m.groupCount() == 0) {
							error("fp1 has not set groups");
							return;
						}
						String __fp2 = fp2;
						for (int j = 1; j <= m.groupCount(); j++) {
							__fp2 = __fp2.replaceAll("<" + (j) + ">", m.group(j));
						}
						final String _fp2 = __fp2;
						String[] files2 = new File(path).list(new FilenameFilter() {
							@Override
							public boolean accept(File dir, String name) {
								return name.matches(_fp2);
							}
						});
						for (int j = 0; j < files2.length; j++) {
							if (!files2[j].equals(files1[i])) {
								String f1 = files1[i];
								String f2 = files2[j];
								if (tfile1 == null) {
									tfile1 = f1;
									tfile2 = f2;
								}
								String[] paths = getPaths(f1, f2);
								if (new File(paths[2]).exists() && new File(paths[3]).exists())
									break;
								file1 = f1;
								file2 = f2;
								break out;
							}
						}
					}
				}
				if (file1 == null && file2 == null && tfile1 != null && tfile2 != null) {
					file1 = tfile1;
					file2 = tfile2;
				}
				if (file2 == null) {
					error("no file match: " + fp2);
					return;
				}
			} else {
				file1 = files1[0];
				String[] files2 = new File(path).list(new FilenameFilter() {
					@Override
					public boolean accept(File dir, String name) {
						return name.matches(fp2);
					}
				});
				for (int i = 0; i < files2.length; i++) {
					if (!files2[i].equals(file1)) {
						String f2 = files2[i];
						if (tfile2 == null) {
							tfile2 = f2;
						}
						String[] paths = getPaths(file1, f2);
						if (new File(paths[2]).exists() && new File(paths[3]).exists())
							break;
						file2 = f2;
						break;
					}
				}
				if (file2 == null && tfile2 != null)
					file2 = tfile2;
				if (file2 == null) {
					error("no file match: " + fp2);
					return;
				}
			}
		}
		this.file1 = file1;
		this.file2 = file2;
		
		String sort = params.get(KEY_SORT);
		if (two && SORT_ASC.equals(sort) || SORT_DESC.equals(sort)) {
			this.sort = sort;
		}
		
		this.pattern1 = params.get(KEY_PATTERN1);
		this.pattern2 = params.get(KEY_PATTERN2);
		this.trim = params.containsKey(KEY_TRIM) && !"false".equalsIgnoreCase(params.get(KEY_TRIM));
	}
	
	private void error(String msg) {
		System.out.println(msg);
//		throw new RuntimeException();
	}
	
	private String[] getPaths(String file1, String file2) {
		String[] paths = new String[4];
		paths[0] = this.targetPath + file1;
		if (!new File(paths[0]).exists()) {
			error("file not exists: " + paths[0]);
			return null;
		}
		if (file2 != null) {
			paths[1] = this.targetPath + file2;
			if (!new File(paths[1]).exists()) {
				error("file not exists: " + paths[1]);
				return null;
			}
			int p1 = file1.lastIndexOf('.');
			int p2 = file2.lastIndexOf('.');
			int p3 = 0;
			for (int i = 0; i<file1.length() && i<file2.length(); i++) {
				char c = file1.charAt(i);
				if (c >= '0' && c <= '9' || p2 >= 0 && i >= p2 || file2.charAt(i) != c)
					break;
				p3++;
			}
			if (p3 <= 1)
				p3 = 0;
			String name = (p1 > 0 ? file1.substring(0, p1) : file1) + "--" + (p2 > 0 ? file2.substring(p3, p2) : file2);
			String ext = p1 > 0 ? file1.substring(p1) : null;
			ext = ext == null ? (p2 > 0 ? file2.substring(p2) : null) : ext;
			ext = ext == null ? ".txt" : ext;
			paths[2] = this.targetPath + name + ".--1--" + ext;
			paths[3] = this.targetPath + name + ".--2--" + ext;
		} else {
			int p1 = file1.lastIndexOf('.');
			String name = (p1 > 0 ? file1.substring(0, p1) : file1);
			String ext = p1 > 0 ? file1.substring(p1) : null;
			ext = ext == null ? ".txt" : ext;
			paths[2] = this.targetPath + name + ".--1--" + ext;
			paths[3] = this.targetPath + name + ".--2--" + ext;
		}
		return paths;
	}
	
	public static int getInteger(String str, int def) {
		try {
			return Integer.parseInt(str, 10);
		} catch (NumberFormatException e) {
		}
		return def;
	}
	
	public static Map<String, String> getParams(String[] args) {
		Map<String, String> params = new HashMap<String, String>();
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];
			if (arg.indexOf('=') > 0) {
				int j = arg.indexOf('=');
				String key = arg.substring(0, j);
				String value = arg.substring(j + 1);
				params.put(key, value);
			} else {
				params.put(arg, null);
			}
		}
		return params;
	}
	
	public static boolean isEmpty(String str) {
		return str == null || str.length() == 0;
	}
	
	private void sort() {
		if (this.sort == null)
			return;
		String[] s = new String[2];
		s[0] = this.file1;
		s[1] = this.file2;
		Arrays.sort(s, new Comparator(SORT_ASC.equals(this.sort)));
		this.file1 = s[0];
		this.file2 = s[1];
	}
	
	private static class Comparator implements java.util.Comparator<String> {
		private int asc;
		public Comparator(boolean asc) {
			this.asc = asc ? 1 : -1;
		}
		@Override
		public int compare(String o1, String o2) {
			o1 = o1 == null ? "" : o1;
			o2 = o2 == null ? "" : o2;
			if (o1.length() == o2.length())
				return asc * o1.compareTo(o2);
			int l1 = o1.length();
			int l2 = o2.length();
			int s = 0;
			int e = 0;
			for (int i = 0; i < l1 && i < l2; i++) {
				if (o1.charAt(i) != o2.charAt(i))
					break;
				s ++;
			}
			for (int i = 0; i < l1 - s && i < l2 - s; i++) {
				if (o1.charAt(l1 - 1 - i) != o2.charAt(l2 - 1 - i))
					break;
				e ++;
			}
			String s1 = o1.substring(s, l1 - e);
			String s2 = o2.substring(s, l2 - e);
			int c1 = getInteger(s1, -1);
			int c2 = getInteger(s2, -1);
			if (c1 != -1 && c2 != -1)
				return asc * (c1 - c2);
			return asc * s1.compareTo(s2);
		}
		
	}
}
